/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.jfab.core.analyzer.AnalyzerInjector;
import org.jfab.core.analyzer.ClassAnalyzer;
import org.jfab.core.analyzer.DefaultAnalyzerInjector;
import org.jfab.core.analyzer.EnumAnalyzer;
import org.jfab.core.analyzer.InterfaceAnalyzer;
import org.jfab.core.io.CGCompilationUnitReader;
import org.jfab.core.model.CGClass;
import org.jfab.core.model.CGCompilationUnit;
import org.jfab.core.model.CGEnum;
import org.jfab.core.model.CGInterface;
import org.jfab.core.model.CGSpec;
import org.jfab.core.util.Compiler;
import org.jfab.core.util.JavaStringCompiler;
import org.jfab.core.util.JavaStringCompilerException;

/**
 * Provides an application to reverse engineer Java source files.
 * 
 * <ol>
 * <li>Read filenames in a directory, or from a file</li>
 * <li>Create a CGSpec</li>
 * <li>For each file in a directory</li>
 * <li>Read file contents</li>
 * <li>Add CGObjects to a CGSpec</li>
 * <li>Write CGSpec to a file</li>
 * </ol>
 */
public final class JavaReverseEngineer implements ReverseEngineer
{
    @Override
    public final CGCompilationUnit reverseEngineerCodeFile(final File file)
    {
        CGCompilationUnit answer = null;

        if (file != null)
        {
            final AnalyzerInjector analyzerInjector = new DefaultAnalyzerInjector();
            final ClassAnalyzer classAnalyzer = analyzerInjector.injectClassAnalyzer();
            final InterfaceAnalyzer interfaceAnalyzer = analyzerInjector.injectInterfaceAnalyzer();
            final EnumAnalyzer enumAnalyzer = analyzerInjector.injectEnumAnalyzer();

            final Compiler<?> compiler = JavaStringCompiler.create();

            final CGCompilationUnitReader cuReader = new CGCompilationUnitReader(interfaceAnalyzer, classAnalyzer,
                    enumAnalyzer, compiler);

            FileReader reader = null;

            try
            {
                reader = new FileReader(file);
                answer = cuReader.read(reader);
            }
            catch (final JavaStringCompilerException e)
            {
                e.printStackTrace();
            }
            catch (final FileNotFoundException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (reader != null)
                {
                    try
                    {
                        reader.close();
                    }
                    catch (final IOException ignore)
                    {
                        // Nothing to do.
                    }
                }
            }
        }

        return answer;
    }

    @Override
    public final CGSpec reverseEngineerCodeFilesInDirectory(final File directory)
    {
        final CGSpec answer = new CGSpec();

        final String[] extensions = { "java", };
        final boolean isRecursive = false;
        final Collection<File> files = FileUtils.listFiles(directory, extensions, isRecursive);

        for (final File file : files)
        {
            if (!"package-info.java".equals(file.getName()))
            {
                System.out.println("processing " + file.getAbsolutePath());
                processFile(file, answer);
            }
        }

        return answer;
    }

    /**
     * @param file Java source file.
     * @param cgSpec Code generator root object.
     */
    private void processFile(final File file, final CGSpec cgSpec)
    {
        final CGCompilationUnit cgCompilationUnit = reverseEngineerCodeFile(file);

        if (cgCompilationUnit instanceof CGClass)
        {
            final CGClass cgClass = (CGClass)cgCompilationUnit;
            cgSpec.getClasses().add(cgClass);
        }
        else if (cgCompilationUnit instanceof CGEnum)
        {
            final CGEnum cgEnum = (CGEnum)cgCompilationUnit;
            cgSpec.getEnums().add(cgEnum);
        }
        else if (cgCompilationUnit instanceof CGInterface)
        {
            final CGInterface cgInterface = (CGInterface)cgCompilationUnit;
            cgSpec.getInterfaces().add(cgInterface);
        }
    }
}
